#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include "mach.h"
#include "blk.h"
#include "exec.h"
#define OS_ORG 0x3000

int old_lflag, old_min, old_time;
struct termios tio;

int
console_init () {
    int fd_err;
    if (tcgetattr (fileno (stdin), &tio) == -1) {
        fprintf (stderr, "Termios error: %s\n", strerror (errno));
        exit (EXIT_FAILURE);
    }
    old_lflag = tio.c_lflag;
    old_min = tio.c_cc[VMIN];
    old_time = tio.c_cc[VTIME];
    tio.c_lflag &= ~(ICANON | ECHO);
    tio.c_cc[VMIN] = 1;
	tio.c_cc[VTIME] = 0;
    (void) tcsetattr (fileno (stdin), TCSANOW, &tio);
    return 0;
}

int
console_reset () {
    tio.c_lflag = old_lflag;
	tio.c_cc[VMIN] = old_min;
	tio.c_cc[VTIME] = old_time;
	(void)tcsetattr (fileno (stdin), TCSANOW, &tio);
    return 0;
}

int
to_little_endian (char* cmem) {
    printf ("Converting memory to little endian.\n");
    int pg_num;
    int pg_ptr;
    char low, high, temp;
    for (int i = 0; i < MEMSZ * sizeof (target_size_t); i+=2) {
        temp = cmem[i];
        cmem[i] = cmem[i+1];
        cmem[i+1] = temp; 
    }
    printf ("Converted.\n");
    return 0;
}

int
load_os (MachineStatus *ms, char *file) {
    char buffer [BUFSIZ];
    int fd_os;
    if ((fd_os = open (file, O_RDONLY)) == -1) {
        fprintf (stderr, "Can't open OS img: %s\n", strerror (errno));
        return -1;
    }
    int n, pg_num, pg_ptr;
    int rd_ptr = 0;
    char* cmem = (char*) ms->memory;
    errno = 0;
    while ((n = read (fd_os, buffer, BUFSIZ)) > 0) {
        if (rd_ptr + n > MEMSZ * sizeof (target_size_t))
            return -1;
        for (int i = 0; i < n; i++) 
            cmem[rd_ptr + i] = buffer[i];
        rd_ptr += n;
    }
    if (n == -1) {
        fprintf (stderr, "Can't read img: %s\n", strerror (errno));
        close (fd_os);
        return -1;
    }
    close (fd_os);
    /* 转换到适用X86的小端存储 */
    to_little_endian (cmem);
    printf ("OS loaded.\n");
    return 0;
}

int
load_obj (MachineStatus *ms, char *file) {

    return 0;
}

int
main (int argc, char *argv[]) {
    BlockManager *blkmgr;
    MachineStatus *ms;
    BasicBlock *start_blk;
    ms = machine_init ();
    console_init ();
    target_size_t *registers = ms->registers;
    load_os (ms, "os.img");
    REG(R_PC) = OS_ORG;
    // load_obj (ms, "a.obj");
    blkmgr = blkmgr_init (ms);
    start_blk = blkmgr_get_block (blkmgr, REG(R_PC));
    //exit (0);
    blk_exec (start_blk);
    console_reset ();
    return  0;
}